#pragma once
#include "./Object3D.hpp"
#include "../opengl/opengl.hpp"
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>

class Entity3D : public Object3D {
public:
    Entity3D() : Object3D(){};
    Entity3D(const Entity3D &) = delete;
    Entity3D &operator=(const Entity3D &) = delete;

public:
    virtual void update() { updateMatrix(); }
    virtual void render() = 0;

public:
    void updateMatrix() {
        using namespace glm;
        if (!updated) {
            modelMatrix = identity<mat4>();
            modelMatrix = glm::scale(modelMatrix, scale);
            modelMatrix = translate(modelMatrix, position);
            modelMatrix = rotate(modelMatrix, rotateAngle, rotateAxis);
            normalMatrix = transpose(inverse(modelMatrix));
            transformBuffer.set_data(&modelMatrix, sizeof(TransformBlock));
            updated = true;
        }
    }

    void setPosition(const glm::vec3 &pos) {
        updated = false;
        position = pos;
        updateMatrix();
    }
    void setPosition(float x, float y, float z) {
        updated = false;
        position = {x, y, z};
        updateMatrix();
    };
    void setScale(const glm::vec3 &s) {
        updated = false;
        scale = s;
        updateMatrix();
    }
    void setScale(float x, float y, float z) {
        updated = false;
        scale = {x, y, z};
        updateMatrix();
    };
    void setRotation(const glm::vec3 &axis, float radAngle) {
        updated = false;
        rotateAngle = radAngle;
        rotateAxis = axis;
        updateMatrix();
    }
    void setRotation(float x, float y, float z, float radAngle) {
        updated = false;
        rotateAngle = radAngle;
        rotateAxis = {x, y, z};
        updateMatrix();
    };

public:
    struct TransformBlock {
        glm::mat4 modelMatrix;
        glm::mat4 normalMatrix;
    };
    opengl::buffer transformBuffer{sizeof(TransformBlock)};

public:
    // scale -> translate(position) -> rotation
    glm::vec3 scale{1.0, 1.0, 1.0};
    glm::vec3 rotateAxis{0.0, 1.0, 0.0};
    float rotateAngle{0.0}; // Radians

public:
    glm::mat4 modelMatrix{glm::identity<glm::mat4>()};
    glm::mat4 normalMatrix{glm::identity<glm::mat4>()};

public:
    bool updated = false;
};